﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Compilex.BaseLibrary;

namespace Compilex.Automata
{
    /// <summary>
    /// Representa um estado em um autômato finito. Cada estado tem um nome, que deve
    /// ser único, e uma lista de transições. Esta classe implementa os operadores
    /// == e !=, além dos métodos Equals e GetHashCode.
    /// </summary>
    public class NFAState : IState<NFAState>
    {
        public String Name { get; set; }
        public bool IsAcceptState { get; set; }

        public SimpleList<ITransitionRule<NFAState>> Transitions { get; set; }

        public NFAState()
        {
            this.Name = "Anonymous state";
            this.IsAcceptState = false;
            this.Transitions = new SimpleList<ITransitionRule<NFAState>>();
        }

        public NFAState(String name)
        {
            this.Name = name;
            this.IsAcceptState = false;
            this.Transitions = new SimpleList<ITransitionRule<NFAState>>();
        }

        public NFAState(String name, bool isAcceptState)
        {
            this.Name = name;
            this.IsAcceptState = isAcceptState;
            this.Transitions = new SimpleList<ITransitionRule<NFAState>>();
        }

        public NFAState(String name, SimpleTransitionRule<NFAState>[] transitions)
        {
            this.Name = name;
            this.IsAcceptState = false;
            this.Transitions = new SimpleList<ITransitionRule<NFAState>>(transitions);
        }
        public NFAState(String name, bool isAcceptState, SimpleTransitionRule<NFAState>[] transitions)
        {
            this.Name = name;
            this.IsAcceptState = isAcceptState;
            this.Transitions = new SimpleList<ITransitionRule<NFAState>>(transitions);
        }

        /// <summary>
        /// Se houver uma transição para esta entrada, retorna o estado destino.
        /// Caso contrário, retorna null.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public NFAState[] GetDestinationStates(string input)
        {
            return (from t in Transitions.ToArray()
                    where t.MatchesInput(input)
                    select t.Destination).ToArray();
        }

        public NFAState TryGetFirstDestinationState(string input)
        {
            NFAState[] destinations = GetDestinationStates(input);
            if (destinations.Length > 0)
                return destinations[0];
            else
                return null;
        }

        public NFAState[] GetEpsilonClosure()
        {
            SimpleList<NFAState> destinations = new SimpleList<NFAState>();
            destinations.Add(this);
            destinations.AddRange(
                from t in Transitions.ToArray()
                where t.IsEpsilon
                select t.Destination);
            bool noMoreStates = false;
            while(!noMoreStates)
            {
                SimpleList<NFAState> newDestinations = new SimpleList<NFAState>();
                for(int i = 0; i < destinations.Count; i++)
                {
                    newDestinations.AddRange(
                    from t in destinations[i].Transitions.ToArray()
                    where t.IsEpsilon
                    select t.Destination);
                }
                bool anyNewStates = false;
                for (int i = 0; i < newDestinations.Count; i++)
                {
                    bool wasAlreadyThere = false;
                    for (int j = 0; j < destinations.Count && !wasAlreadyThere; j++)
                    {
                        if (newDestinations[i].Name == destinations[j].Name)
                            wasAlreadyThere = true;
                    }
                    if (!wasAlreadyThere)
                    {
                        anyNewStates = true;
                        destinations.Add(newDestinations[i]);
                    }
                }
                if (anyNewStates)
                    noMoreStates = false;
                else
                    noMoreStates = true;
            }
            return destinations.ToArray();
        }

        public void AddTransition(char input, NFAState destination)
        {
            AddTransition(new SimpleTransitionRule<NFAState>(input, destination));
        }

        public void AddTransition(string input, NFAState destination)
        {
            AddTransition(new SimpleTransitionRule<NFAState>(input, destination));
        }

        public void AddTransition(Predicate<string> predicate, NFAState Destination)
        {
            AddTransition(new PredicateTransitionRule<NFAState>(predicate, Destination));
        }

        public void AddTransition(ITransitionRule<NFAState> rule)
        {
            Transitions.Add(rule);
        }

        public void AddEpsilonTransition(NFAState destination)
        {
            Transitions.Add(new EpsilonTransitionRule<NFAState>(destination));
        }


        public override string ToString()
        {
            return Name;
        }
    }
}
